# -*- coding: utf-8 -*-
"""
 * @file inferyolov5.py
 * @author SDK_TEAM
 * @brief
 * @version 0.1
 * @date 2022-11-3
 * Copyright:
 * © 2018 北京灵汐科技有限公司 版权所有。
 * 注意：以下内容均为北京灵汐科技有限公司原创，未经本公司允许，不得转载，否则将视为侵权；对于不遵守此声明或者其他违法使用以下内容者，本公司依法保留追究权。
 * © 2018 Lynxi Technologies Co., Ltd. All rights reserved.
 * NOTICE: All information contained here is, and remains the property of Lynxi. This file can not
 * be copied or distributed without the permission of Lynxi Technologies Co., Ltd.
"""

import argparse
import pylynchipsdk as sdk
import numpy as np
import threading
import _thread
import struct

import os
import sys, getopt
import time
sys.path.append('../../../')
from common.python.plugin_utils import *
import common.python.bufferpool as bufferpool
import common.python.common as common
from ctypes import *
from typing import cast
from collections import deque

g_time_start = time.time()
g_time_end = time.time()
channel_deque = []
mCount = {}
mStart = {}
mEnd = {}

totalCount = 0
g_time_start = 0
g_time_end = 0

class EncodeData():
    def __init__(self, device_id, channel_id):
        self.device_id = device_id
        self.channel_id = channel_id
        pass

def free_to_pool_callback(params):
    vdec_out_pool = params[0]
    data = params[1]
    vdec_out_pool.push(data)
    return 0

def on_demux_callback(packet):
    if packet.eos != True:
        ret = sdk.lyn_demux_free_packet(packet)
        common.error_check(ret, "lyn_demux_free_packet")
    return 0


def save_file_callback(encode_data):
    packet = encode_data.packet
    data_size, ret = sdk.lyn_enc_get_remote_packet_valid_size(packet)
    common.error_check(ret, "lyn_enc_get_remote_packet_valid_size")
    data = np.zeros(data_size, dtype=np.byte)
    data_ptr = sdk.lyn_numpy_to_ptr(data)
    ret = sdk.lyn_memcpy(data_ptr, packet.data, data_size, sdk.lyn_memcpy_dir_t.ServerToClient)
    common.error_check(ret, "lyn_memcpy")
    encode_data.recv_pool.push(packet.data)

    # with open(encode_data.file_path, "ab") as f:
    #     f.write(data.tobytes())

    v = str(encode_data.device_id) + str(encode_data.channel_id)
    mCount[v] = mCount[v] + 1

    # print_v = 'v: %s. value: %s.' % (v, mCount[v])
    # print(print_v)

    # #every channel fps

    # if (mCount[v] == 1):
    #     global g_time_start
    #     g_time_start = time.time()
    #     mStart[v] = g_time_start
    #     # print('v: %s. g_time_start: %s.' % (v, g_time_start))

    # if (mCount[v] == 500):
    #     global g_time_end
    #     g_time_end = time.time()
    #     mEnd[v] = g_time_end
    #     g_time = mEnd[v] - mStart[v]
    #     frame_rate = "{:.2f}".format((500*1) / g_time)
    #     # print('v: %s. g_time_end: %s' % (v, g_time_end))

    #     print_fps = 'device: %s. channel: %s. frame_rate: %s' % (v[0:1], v[1:], frame_rate)
    #     print(print_fps)

    # #all channel fps
    # global totalCount
    # global g_time_start
    # global g_time_end
    # totalCount = totalCount + 1
    # if (totalCount == 1):
    #     g_time_start = time.time()
    # if (totalCount == 500):
    #     g_time_end = time.time()
    #     g_time = g_time_end - g_time_start
    #     frame_rate = "{:.2f}".format((500*1) / g_time)
    #     print_fps = 'frame_rate: %s' % (frame_rate)
    #     print(print_fps)


    return ret

def set_even(num):
    if num % 2 != 0:
        res = num + 1
    else:
        res = num
    return res

def set_padding_data(video_width, video_heigh, model_width, model_height):
    if (video_width > video_heigh):
        resize_width = model_width
        pad_x = 0
        # resize_height = (video_heigh * model_width / video_width) & ~1
        resize_height = set_even((video_heigh * model_width / video_width))
        pad_y = (model_height - resize_height) / 2
    else:
        resize_height = model_height
        pad_y = 0
        # resize_width = (video_width * model_width / video_heigh) & ~1
        resize_width = set_even((video_width * model_width / video_heigh))
        pad_x = (model_width - resize_width) / 2

    return int(resize_width), int(resize_height), int(pad_x), int(pad_y)

class Inference:
    def __init__(self, attr):
        self.__thread = ""
        self.attr = attr
        self.label_list, self.label_info_dev = create_label_list_with_names(attr.label_file)

    def infer(self, did, cid):
        sdk.lyn_set_current_context(self.attr.ctx)

        # print_string = 'device: %s. channel: %s.' % (did,cid)
        # print(print_string)

        # time.sleep(0.001)

        # 校验用户设置的 device id
        if not common.check_device_id(did):
            return False

        ret = sdk.lyn_register_error_handler(common.default_stream_error_handle, None)
        common.error_check(ret, "lyn_register_error_handler")
        # 创建 stream
        vdec_send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        vdec_recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        ipe_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        apu_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        post_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        venc_send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        venc_recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        # 创建 event
        vdec_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        ipe_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        venc_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")

        # 打开解封装句柄
        demuxer, ret = sdk.lyn_demux_open(self.attr.video_path)
        common.error_check(ret, "lyn_demux_open")
        demux_info, ret = sdk.lyn_demux_get_codec_para(demuxer)
        common.error_check(ret, "lyn_demux_get_codec_para")
        # print("video frame (width, height) = ", (demux_info.width, demux_info.height))
        # 设置解码参数
        vdecAttr = sdk.lyn_vdec_attr_t()
        vdecAttr.codec_id = demux_info.codec_id
        vdecAttr.output_fmt = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        vdecAttr.scale = sdk.lyn_scale_t.SCALE_NONE
        vdecoder, ret = sdk.lyn_vdec_open(vdecAttr)
        common.error_check(ret, "lyn_vdec_open")
        vdec_out_info, ret = sdk.lyn_vdec_get_out_info(demux_info, vdecAttr)
        common.error_check(ret, "vdec_out_info")
        vdec_out_size = vdec_out_info.predict_buf_size
        # 生成输出文件路径
        file_name, fe_name = os.path.splitext(self.attr.video_path)
        file_path = file_name + "_py_result.264"
        # print("result save to ", file_path)
        # if os.path.exists(file_path):
        #     os.remove(file_path)

        # 设置编码参数
        venc_attr = sdk.lyn_venc_attr_t()
        ret = sdk.lyn_venc_set_default_params(venc_attr)
        common.error_check(ret, "lyn_venc_set_default_params")
        venc_attr.codec_type = sdk.lyn_codec_id_t.LYN_CODEC_ID_H264
        venc_attr.width = vdec_out_info.width
        venc_attr.height = vdec_out_info.height
        venc_attr.bit_depth = 8
        venc_attr.bframes_num = 0
        venc_attr.pframes_num = 5
        venc_attr.input_format = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        venc_attr.target_bitrate = 6000000
        venc_handle, ret = sdk.lyn_venc_open(venc_attr)
        common.error_check(ret, "lyn_vdec_open")

        # 加载模型
        model, ret = sdk.lyn_load_model(self.attr.model_path)
        common.error_check(ret, "lyn_load_model")
        model_desc, ret = sdk.lyn_model_get_desc(model)
        common.error_check(ret, "lyn_model_get_desc")
        batch_size = model_desc.inputTensorAttrArray[0].batchSize
        model_width = model_desc.inputTensorAttrArray[0].dims[2]
        model_height = model_desc.inputTensorAttrArray[0].dims[1]
        ipe_output_size = model_width * model_height * model_desc.inputTensorAttrArray[0].dims[3]
        apu_output_size = model_desc.outputDataLen

        # 创建 IPE 相关结构体
        ipe_input_desc, ret = sdk.lyn_ipe_create_pic_desc()
        common.error_check(ret, "lyn_ipe_create_pic_desc ipe_input_desc")
        ipe_output_desc, ret = sdk.lyn_ipe_create_pic_desc()
        common.error_check(ret, "lyn_ipe_create_pic_desc ipe_output_desc")
        ipe_config, ret = sdk.lyn_ipe_create_config_desc()
        common.error_check(ret, "lyn_ipe_create_config_desc")
        ret = sdk.lyn_ipe_reset_pic_desc(ipe_input_desc)
        common.error_check(ret, "lyn_ipe_reset_pic_desc")
        ret = sdk.lyn_ipe_reset_pic_desc(ipe_output_desc)
        common.error_check(ret, "lyn_ipe_reset_pic_desc")
        ret = sdk.lyn_ipe_reset_config_desc(ipe_config)
        common.error_check(ret, "lyn_ipe_reset_config_desc")
        resize_width, resize_height, pad_x, pad_y = set_padding_data(vdec_out_info.width, vdec_out_info.height, model_width, model_height)
        # 创建内存池
        vdec_out_pool = bufferpool.buffer_pool(vdec_out_size, 5)
        ipe_output_mem_pool = bufferpool.buffer_pool(ipe_output_size * batch_size, 5)
        apu_output_mem_pool = bufferpool.buffer_pool(apu_output_size * batch_size, 5)
        venc_recv_pool = bufferpool.buffer_pool(vdec_out_size, 5)

        # 加载 Plugin
        self.osd_plugin, ret = sdk.lyn_plugin_register(self.attr.osd_plugin)
        common.error_check(ret, "lyn_plugin_register")
        self.post_plugin, ret = sdk.lyn_plugin_register(self.attr.plugin_path)
        common.error_check(ret, "lyn_plugin_register")
        boxes_info, ret = sdk.lyn_malloc(ctypes.sizeof(Box))
        common.error_check(ret, "malloc")

        loop_eos = False
        enc_head_flag = True
        while loop_eos == False:
            # 解封装
            packet, ret = sdk.lyn_demux_read_packet(demuxer)
            if ret != 0:
                packet.eos = True
                loop_eos = True

            ret = sdk.lyn_vdec_send_packet_async(vdec_send_stream, vdecoder, packet)
            common.error_check(ret, "lyn_vdec_send_packet_async")
            ret = sdk.lyn_stream_add_callback(vdec_send_stream, on_demux_callback, packet)
            common.error_check(ret, "lyn_stream_add_callback")

            recv_frame = sdk.lyn_frame_t()
            recv_frame.data = vdec_out_pool.pop()
            recv_frame.size = vdec_out_size
            recv_frame.eos = loop_eos

            ret = sdk.lyn_vdec_recv_frame_async(vdec_recv_stream, vdecoder, recv_frame)
            common.error_check(ret, "lyn_vdec_recv_frame_async")
            # 等待解码完成
            ret = sdk.lyn_record_event(vdec_recv_stream, vdec_event)
            common.error_check(ret, "lyn_record_event")
            ret = sdk.lyn_stream_wait_event(ipe_stream, vdec_event)
            common.error_check(ret, "lyn_stream_wait_event")

            # 设置IPE参数，并进行IPE处理
            ipe_out_data = ipe_output_mem_pool.pop()
            ret = sdk.lyn_ipe_set_input_pic_desc(ipe_input_desc, recv_frame.data, vdec_out_info.width,
                                                vdec_out_info.height, vdecAttr.output_fmt) # set ipe input
            common.error_check(ret, "lyn_ipe_set_input_pic_desc")
            ret = sdk.lyn_ipe_set_output_pic_data(ipe_output_desc, ipe_out_data) # set ipe output
            common.error_check(ret, "lyn_ipe_set_output_pic_data")
            ret = sdk.lyn_ipe_set_resize_config(ipe_config, resize_width, resize_height) # resize to model require
            common.error_check(ret, "lyn_ipe_set_resize_config")
            ret = sdk.lyn_ipe_set_pad_config(ipe_config, pad_y, pad_x, pad_y, pad_x, 114, 114, 114)
            common.error_check(ret, "lyn_ipe_set_pad_config")
            ret = sdk.lyn_ipe_set_c2c_config(ipe_config, sdk.lyn_pixel_format_t.LYN_PIX_FMT_RGB24, 0)
            common.error_check(ret, "lyn_ipe_set_c2c_config")
            ret = sdk.lyn_ipe_cal_output_pic_desc(ipe_output_desc, ipe_input_desc, ipe_config, 0)
            common.error_check(ret, "lyn_ipe_cal_output_pic_desc")
            ret = sdk.lyn_ipe_process_async(ipe_stream, ipe_input_desc, ipe_output_desc, ipe_config)
            common.error_check(ret, "lyn_ipe_process_async")

            # 等待IPE处理完成
            ret = sdk.lyn_record_event(ipe_stream, ipe_event)
            common.error_check(ret, "lyn_record_event")
            ret = sdk.lyn_stream_wait_event(apu_stream, ipe_event)
            common.error_check(ret, "lyn_stream_wait_event")


            # apu 推理
            apu_output = apu_output_mem_pool.pop()
            ret = sdk.lyn_execute_model_async(apu_stream, model, ipe_out_data, apu_output, batch_size)
            common.error_check(ret, "lyn_execute_model_async")

            ret = sdk.lyn_stream_add_callback(apu_stream, free_to_pool_callback, [ipe_output_mem_pool, ipe_out_data])
            common.error_check(ret, "lyn_stream_add_callback")

            # 在 APU 推理完之后，调用 Plugin 进行后处理
            ret = sdk.lyn_record_event(apu_stream, apu_event)
            common.error_check(ret, "lyn_record_event")
            ret = sdk.lyn_stream_wait_event(post_stream, apu_event)
            common.error_check(ret, "lyn_stream_wait_event")
            format = int(sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12)
            pythonapi.PyCapsule_GetPointer.restype = c_void_p
            pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]
            apu_data_ptr = pythonapi.PyCapsule_GetPointer(apu_output, None)
            boxes_info_ptr = pythonapi.PyCapsule_GetPointer(boxes_info, None)
            frame_data_ptr = pythonapi.PyCapsule_GetPointer(recv_frame.data, None)
            post_para = YoloxPostProcessInfo_t()
            post_para.height = model_height
            post_para.width = model_width
            post_para.ori_height = demux_info.height
            post_para.ori_width = demux_info.width
            post_para.score_threshold = 0.25
            post_para.nms_threshold = 0.45
            post_para.nms_top_k = 500
            post_para.is_pad_resize = 1
            post_para.output_tensor = apu_data_ptr
            post_para.class_num = self.label_list.labelNum
            post_para.labelList = self.label_list
            post_para.boxesInfo = boxes_info_ptr
            post_para.anchorSize = 3

            yolox_data = ctypes.string_at(
                ctypes.addressof(post_para), ctypes.sizeof(post_para)
            )

            ret = sdk.lyn_plugin_run_async(
                post_stream,
                self.post_plugin,
                "lynYoloxPostProcess",
                yolox_data,
                ctypes.sizeof(YoloxPostProcessInfo_t),
            )
            common.error_check(ret, "lyn_plugin_run_async")
            osd_para = struct.pack(
                "1P3i1P4i",
                boxes_info_ptr,
                demux_info.width,
                demux_info.height,
                sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
                frame_data_ptr,
                24,
                4,
                2,
                4,
            )

            common.error_check(
                sdk.lyn_plugin_run_async(
                    post_stream,
                    self.osd_plugin,
                    "lynDrawBoxAndText",
                    osd_para,
                    len(osd_para),
                )
            )
            common.error_check(ret, "lyn_plugin_run_async")
            ret = sdk.lyn_stream_add_callback(post_stream, free_to_pool_callback, [apu_output_mem_pool, apu_output])
            common.error_check(ret, "lyn_stream_add_callback")

            # 在 plugin 处理完之后，进行编码操作
            ret = sdk.lyn_record_event(post_stream, venc_event)
            common.error_check(ret, "lyn_record_event")
            ret = sdk.lyn_stream_wait_event(venc_send_stream, venc_event)
            common.error_check(ret, "lyn_stream_wait_event")
            if enc_head_flag: # 第一帧编码
                enc_head_flag = False
                enc_packet = sdk.lyn_packet_t()
                enc_packet.size = vdec_out_size
                enc_packet.data = venc_recv_pool.pop()
                encode_data = EncodeData(did, cid)
                encode_data.packet = enc_packet
                encode_data.recv_pool = venc_recv_pool
                encode_data.file_path = file_path

                ret = sdk.lyn_venc_get_paramsset_async(venc_recv_stream, venc_handle, enc_packet)
                common.error_check(ret, "lyn_venc_get_paramsset_async")
                ret = sdk.lyn_stream_add_callback(venc_recv_stream, save_file_callback, encode_data)
                common.error_check(ret, "lyn_stream_add_callback")


            ret = sdk.lyn_venc_sendframe_async(venc_send_stream, venc_handle, recv_frame)
            common.error_check(ret, "lyn_venc_sendframe_async")
            ret = sdk.lyn_stream_add_callback(venc_send_stream, free_to_pool_callback, [vdec_out_pool, recv_frame.data])
            common.error_check(ret, "lyn_stream_add_callback")
            enc_packet = sdk.lyn_packet_t()
            enc_packet.size = vdec_out_size
            enc_packet.eos = loop_eos
            enc_packet.data = venc_recv_pool.pop()
            encode_data = EncodeData(did, cid)
            encode_data.packet = enc_packet
            encode_data.recv_pool = venc_recv_pool
            encode_data.file_path = file_path
            ret = sdk.lyn_venc_recvpacket_async(venc_recv_stream, venc_handle, enc_packet)

            common.error_check(ret, "lyn_venc_recvpacket_async")
            ret = sdk.lyn_stream_add_callback(venc_recv_stream, save_file_callback, encode_data)
            common.error_check(ret, "lyn_stream_add_callback")

            # print_string = 'device: %s. channel: %s. doing' % (did,cid)
            # print(print_string)


        # 等待流处理完
        ret = sdk.lyn_synchronize_stream(vdec_send_stream)
        common.error_check(ret, "lyn_synchronize_stream")
        ret = sdk.lyn_synchronize_stream(vdec_recv_stream)
        common.error_check(ret, "lyn_synchronize_stream")
        ret = sdk.lyn_synchronize_stream(ipe_stream)
        common.error_check(ret, "lyn_synchronize_stream")
        ret = sdk.lyn_synchronize_stream(apu_stream)
        common.error_check(ret, "lyn_synchronize_stream")
        ret = sdk.lyn_synchronize_stream(venc_send_stream)
        common.error_check(ret, "lyn_synchronize_stream")
        ret = sdk.lyn_synchronize_stream(venc_recv_stream)
        common.error_check(ret, "lyn_synchronize_stream")
        ret = sdk.lyn_synchronize_stream(post_stream)
        common.error_check(ret, "lyn_synchronize_stream")
        ret = sdk.lyn_destroy_stream(vdec_send_stream)
        common.error_check(ret, "lyn_destroy_stream")
        ret = sdk.lyn_destroy_stream(vdec_recv_stream)
        common.error_check(ret, "lyn_destroy_stream")
        ret = sdk.lyn_destroy_stream(ipe_stream)
        common.error_check(ret, "lyn_destroy_stream")
        ret = sdk.lyn_destroy_stream(apu_stream)
        common.error_check(ret, "lyn_destroy_stream")
        ret = sdk.lyn_destroy_stream(venc_send_stream)
        common.error_check(ret, "lyn_destroy_stream")
        ret = sdk.lyn_destroy_stream(venc_recv_stream)
        common.error_check(ret, "lyn_destroy_stream")
        ret = sdk.lyn_destroy_stream(post_stream)
        common.error_check(ret, "lyn_destroy_stream")

        # destory ipe desc and config
        ret = sdk.lyn_ipe_destroy_pic_desc(ipe_input_desc)
        common.error_check(ret, "lyn_ipe_destroy_pic_desc")
        ret = sdk.lyn_ipe_destroy_pic_desc(ipe_output_desc)
        common.error_check(ret, "lyn_ipe_destroy_pic_desc")
        ret = sdk.lyn_ipe_destroy_config_desc(ipe_config)
        common.error_check(ret, "lyn_ipe_destroy_config_desc")

        # 关闭解码器与编码器
        ret = sdk.lyn_demux_close(demuxer)
        common.error_check(ret, "lyn_demux_close")
        ret = sdk.lyn_vdec_close(vdecoder)
        common.error_check(ret, "lyn_vdec_close")
        ret = sdk.lyn_venc_close(venc_handle)
        common.error_check(ret, "lyn_venc_close")

        # 释放 Pool 内存
        sdk.lyn_free(boxes_info)
        sdk.lyn_free(self.label_info_dev)
        vdec_out_pool.free_buffers()
        ipe_output_mem_pool.free_buffers()
        apu_output_mem_pool.free_buffers()
        venc_recv_pool.free_buffers()

        # 卸载模型
        ret = sdk.lyn_unload_model(model)
        common.error_check(ret, "lyn_unload_model")

        ret = sdk.lyn_plugin_unregister(self.post_plugin)
        common.error_check(ret, "lyn_plugin_unregister")
        ret = sdk.lyn_plugin_unregister(self.osd_plugin)
        common.error_check(ret, "lyn_plugin_unregister")


    def run(self):
        self.__thread = threading.Thread(target=self.infer, args=(self.attr.device_id,self.attr.channel_id))
        self.__thread.start()
        print("start")

    def close(self):
        if self.__thread.is_alive():
            self.__thread.join()
        print("__thread----------finish")

class Channel_attr:
    def __init__(self) -> None:
        self.model_path = ""
        self.video_path = ""
        self.plugin_path = ""
        self.osd_plugin = ""
        self.label_file = ""
        self.device_id = 0
        self.channel_id = 0
        self.ctx = ""

def usage():
    print("usage: python3 main.py")
    print("\t -m    model_path, eg: eg: ../../../model/yolov5s_b1_h640_w640_c3_iuint8_ofloat16_0000_x7_quant/Net_0")
    print("\t -v    video_path, eg: ../../../data/mulchan_yolo5s_thread_sample/MOT16-09.mp4")
    print("\t -p    post plugin_path, eg: ../../../common/plugin/postprocess/cross_compiled_lib/libYolov5X7PostPlugin.so")
    print("\t -o    osd plugin_path, eg: ../../../common/plugin/osd/lib/libOsdPlugin.so")
    print("\t -l    label_file, eg: ../../../common/plugin/postprocess/yolox/label/label.v5x7.json")
    print("\t -d    device id, default 0")
    print("\t -c    channel count, default 1")
    print("\t -h    show help")
    print("\t -?    show help")

def main(argv):
    try:
        opts, args = getopt.getopt(argv, "m:v:p:o:l:d:c:h?")
    except getopt.GetoptError:
        usage()
        return 0

    attr = Channel_attr()
    channel_count = 1
    device_num = 1

    for opt, arg in opts:
        if opt in("-h", "-?"):
            usage()
            return 0
        elif opt == "-m":
            attr.model_path = arg
        elif opt == "-v":
            attr.video_path = arg
        elif opt == "-p":
            attr.plugin_path = arg
        elif opt == "-o":
            attr.osd_plugin = arg
        elif opt == "-l":
            attr.label_file = arg
        elif opt == "-d":
            dev_id = int(arg)
        elif opt == "-c":
            channel_count = int(arg)
        else:
            usage()
            return 0

    if (attr.model_path == ""):
        print("input file miss!!!")
        usage()
        return 0

    device_num = 1
    channel_deque = []
    ctx_deque = deque()
    for i in range(device_num):
        context, ret = sdk.lyn_create_context(dev_id)
        common.error_check(ret, "lyn_create_context")
        ctx_deque.append(context)
        for j in range(channel_count):
            attr.device_id = dev_id
            attr.channel_id = j
            attr.ctx = context
            mCount[str(attr.device_id) + str(j)] = 0
            myInference = Inference(attr)
            myInference.run()
            channel_deque.append(myInference)

    for it in channel_deque:
        it.close()

    for ct in ctx_deque:
        ret = sdk.lyn_destroy_context(ct)
        common.error_check(ret, "lyn_destroy_context")

if __name__ == "__main__":
    start = time.time()
    main(sys.argv[1:])
    end = time.time()
    print("need time: ", end - start)

# 多线程